package system

import (
	"bytes"
	"fmt"
	consts "qrsj-assistant/backend/const"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"
	"unsafe"

	"golang.org/x/sys/windows"
)

var (
	SW_SHOW    = 5 // 用于激活并显示窗口。如果窗口最小化或最大化，则系统会将其恢复到其原始大小和位置。首次显示窗口时应使用此标志
	SW_RESTORE = 9 // 用于恢复并显示最小化的窗口
)

var (
	user32 = syscall.NewLazyDLL("user32.dll")
	gdi32  = syscall.NewLazyDLL("gdi32.dll")
)

var (
	procGetWindowDC         = user32.NewProc("GetWindowDC")
	procGetDesktopWindow    = user32.NewProc("GetDesktopWindow")
	procGetWindowRect       = user32.NewProc("GetWindowRect")
	procFindWindowW         = user32.NewProc("FindWindowW")
	procSetROP2             = gdi32.NewProc("SetROP2")
	procCreatePen           = gdi32.NewProc("CreatePen")
	procSelectObject        = gdi32.NewProc("SelectObject")
	procRectangle           = gdi32.NewProc("Rectangle")
	procDeleteObject        = gdi32.NewProc("DeleteObject")
	procReleaseDC           = user32.NewProc("ReleaseDC")
	procEnumWindows         = user32.NewProc("EnumWindows")
	procGetClassNameW       = user32.NewProc("GetClassNameW")
	procGetWindowTextW      = user32.NewProc("GetWindowTextW")
	procShowWindow          = user32.NewProc("ShowWindow")
	procSetForegroundWindow = user32.NewProc("SetForegroundWindow")
)

type Rect struct {
	Left, Top, Right, Bottom int32
}

type WindowInfo struct {
	Hwnd windows.HWND `json:"hwnd"`
	Name string       `json:"name"`
}

// 窗口矩形闪烁
func DrawWindowRectangle(hwnd uintptr, thickness int, color uint32, shouldBlink bool) {
	if thickness == 0 {
		thickness = 3
	}

	hdc, _, _ := procGetWindowDC.Call(hwnd)
	defer procReleaseDC.Call(hwnd, hdc)

	var rect Rect
	procGetWindowRect.Call(hwnd, uintptr(unsafe.Pointer(&rect)))

	screenHwnd, _, _ := procGetDesktopWindow.Call()
	screenDC, _, _ := procGetWindowDC.Call(screenHwnd)
	defer procReleaseDC.Call(screenHwnd, screenDC)

	oldRop, _, _ := procSetROP2.Call(screenDC, 10) // 10:R2_NOTXORPEN

	pen, _, _ := procCreatePen.Call(0, uintptr(thickness), uintptr(color))
	oldPen, _, _ := procSelectObject.Call(screenDC, pen)
	defer procDeleteObject.Call(pen)
	defer procSelectObject.Call(screenDC, oldPen)

	procRectangle.Call(screenDC, uintptr(rect.Left), uintptr(rect.Top), uintptr(rect.Right), uintptr(rect.Bottom))

	if shouldBlink {
		time.Sleep(300 * time.Millisecond)
		procRectangle.Call(screenDC, uintptr(rect.Left), uintptr(rect.Top), uintptr(rect.Right), uintptr(rect.Bottom))
		procSetROP2.Call(screenDC, oldRop)
	}
}

// 通过类名和窗口名获取窗口句柄
func FindWindow(className string, windowName string) (
	hwnd windows.HWND, err error) {
	classNamePtr, _ := syscall.UTF16PtrFromString(className)
	windowNamePtr, _ := syscall.UTF16PtrFromString(windowName)
	ClassNameUptr := uintptr(unsafe.Pointer(classNamePtr))
	WindowNameUptr := uintptr(unsafe.Pointer(windowNamePtr))
	if className == "" {
		ClassNameUptr = 0
	}
	if windowName == "" {
		WindowNameUptr = 0
	}
	r0, _, e := procFindWindowW.Call(ClassNameUptr, WindowNameUptr)
	hwnd = windows.HWND(r0)
	if hwnd == 0 {
		err = e
	}
	return
}

// 通过类型和窗口名获取进程ID
func FindWindowProcessID(className string, windowName string) int {
	var pid uint32
	hwnd, err := FindWindow(className, windowName)
	if err != nil {
		return 0
	}
	windows.GetWindowThreadProcessId(hwnd, &pid)
	return int(pid)
}

func EnumWindows(enumFunc uintptr, lParam uintptr) bool {
	ret, _, _ := procEnumWindows.Call(enumFunc, lParam)
	return ret != 0
}

func GetClassName(hwnd windows.HWND) (string, error) {
	className := make([]uint16, 256)
	ret, _, err := procGetClassNameW.Call(uintptr(hwnd), uintptr(unsafe.Pointer(&className[0])), uintptr(len(className)))
	if ret == 0 {
		return "", err
	}
	return syscall.UTF16ToString(className), nil
}

func GetWindowText(hwnd windows.HWND) (string, error) {
	windowText := make([]uint16, 256)
	ret, _, err := procGetWindowTextW.Call(uintptr(hwnd), uintptr(unsafe.Pointer(&windowText[0])), uintptr(len(windowText)))
	if ret == 0 {
		return "", err
	}
	return syscall.UTF16ToString(windowText), nil
}

// 获取所有窗口
func FindAllWindows(className string, windowName string) ([]WindowInfo, error) {
	var windowsList []WindowInfo
	enumFunc := syscall.NewCallback(func(hwnd windows.HWND, lParam uintptr) uintptr {
		cName, _ := GetClassName(hwnd)
		wName, _ := GetWindowText(hwnd)

		if (className == "" || cName == className) && (windowName == "" || wName == windowName) {
			windowsList = append(windowsList, WindowInfo{Hwnd: hwnd, Name: wName})
		}
		return 1 // continue enumeration
	})

	if !EnumWindows(enumFunc, 0) {
		return nil, fmt.Errorf("failed to enumerate windows")
	}

	return windowsList, nil
}

// 激活窗口
func ActivateWindow(hwnd uintptr) error {
	// 还原并显示窗口
	ret, _, err := procShowWindow.Call(hwnd, uintptr(SW_RESTORE))
	if ret == 0 {
		return err
	}
	// 将窗口设置为前台
	ret, _, err = procSetForegroundWindow.Call(hwnd)
	if ret == 0 {
		return err
	}
	return nil
}

// getModuleBaseAddress 通过模块名获取模块基址
//
//	pHandle - 进程句柄
//	moduleName - 模块名
//
// 返回值：
//
//	模块基址
//	错误
func getModuleBaseAddress(pHandle windows.Handle, moduleName string) (uintptr, error) {
	var hModules [1024]windows.Handle
	var cbNeeded uint32

	// 枚举进程中的模块
	if err := windows.EnumProcessModules(pHandle, &hModules[0], uint32(unsafe.Sizeof(hModules)), &cbNeeded); err != nil {
		return 0, err
	}

	// 遍历模块，寻找匹配的模块名
	for i := 0; i < int(cbNeeded/uint32(unsafe.Sizeof(hModules[0]))); i++ {
		var moduleFileName [260]uint16
		if err := windows.GetModuleBaseName(pHandle, hModules[i], &moduleFileName[0], uint32(len(moduleFileName))); err != nil {
			return 0, err
		}

		// 检查模块名是否匹配
		if windows.UTF16ToString(moduleFileName[:]) == moduleName {
			return uintptr(hModules[i]), nil
		}
	}

	return 0, fmt.Errorf("未找到模块")
}

// ReadMultiLevelPointerAddress 读取多级指针指向的地址
//
//	hwnd - 窗口句柄
//	baseAddress - 基址
//	offsets - 偏移量
//	hasModuleBase - 是否需要模块基址"七日世界.exe"
//
// 返回值：
//
//	address - 最终地址
//	err - 错误
func ReadMultiLevelPointerAddress(hwnd uintptr, baseAddress uintptr, offsets []uintptr, hasModuleBase bool) (address uintptr, err error) {
	address = baseAddress
	var pid uint32
	windows.GetWindowThreadProcessId(windows.HWND(hwnd), &pid)
	// 打开进程
	pHandle, err := windows.OpenProcess(0x000F0000|0x00100000|0xFFFF, true, pid)
	if err != nil {
		fmt.Printf("获取进程句柄失败=%v\n", err)
		return 0, err
	}
	defer windows.CloseHandle(pHandle)
	if hasModuleBase {
		// 获取模块基址
		moduleBase, err := getModuleBaseAddress(pHandle, consts.ModuleName)
		if err != nil {
			fmt.Printf("获取模块基址失败=%v\n", err)
			return 0, err
		}
		address = moduleBase + address
	}
	// 逐级解析指针
	for _, offset := range offsets {
		var buffer uintptr
		err := windows.ReadProcessMemory(
			pHandle,
			address,
			(*byte)(unsafe.Pointer(&buffer)),
			unsafe.Sizeof(buffer),
			nil,
		)
		if err != nil {
			return 0, err
		}
		address = buffer + offset
	}
	return address, nil
}

// ReadMultiLevelPointerVal 读取多级指针最终的值
//
//	hwnd - 窗口句柄
//	baseAddress - 基址
//	offsets - 偏移量
//	hasModuleBase - 是否需要模块基址"七日世界.exe"
//
// 返回值：
//
//	finalValue - 最终地址指向的值
//	address - 最终地址
//	pHandle - 进程句柄
//	err - 错误
func ReadMultiLevelPointerVal(hwnd uintptr, baseAddress uintptr, offsets []uintptr, hasModuleBase bool) (finalValue uintptr, address uintptr, err error) {
	address = baseAddress
	var pid uint32
	windows.GetWindowThreadProcessId(windows.HWND(hwnd), &pid)
	// 打开进程
	pHandle, err := windows.OpenProcess(0x000F0000|0x00100000|0xFFFF, true, pid)
	if err != nil {
		fmt.Printf("获取进程句柄失败=%v\n", err)
		return 0, 0, err
	}
	defer windows.CloseHandle(pHandle)
	if hasModuleBase {
		// 获取模块基址
		moduleBase, err := getModuleBaseAddress(pHandle, consts.ModuleName)
		if err != nil {
			fmt.Printf("获取模块基址失败=%v\n", err)
			return 0, 0, err
		}
		address = moduleBase + address
	}
	// 逐级解析指针
	for _, offset := range offsets {
		var buffer uintptr
		err := windows.ReadProcessMemory(
			pHandle,
			address,
			(*byte)(unsafe.Pointer(&buffer)),
			unsafe.Sizeof(buffer),
			nil,
		)
		if err != nil {
			return 0, 0, err
		}
		address = buffer + offset
	}
	// 读取最终地址指向的值
	err = windows.ReadProcessMemory(
		pHandle,
		address,
		(*byte)(unsafe.Pointer(&finalValue)),
		unsafe.Sizeof(finalValue),
		nil,
	)
	if err != nil {
		return 0, 0, err
	}
	return finalValue, address, nil
}

// ReadMemoryValues 读取指定地址的值(单个地址)
//
//	hwnd - 窗口句柄
//	address - 地址
//
// 返回值：
//
//	value - 地址的值
//	err - 错误
func ReadMemoryValues(hwnd uintptr, address uintptr) (value uintptr, err error) {
	var pid uint32
	windows.GetWindowThreadProcessId(windows.HWND(hwnd), &pid)
	// 打开进程
	pHandle, err := windows.OpenProcess(0x000F0000|0x00100000|0xFFFF, true, pid)
	if err != nil {
		fmt.Printf("获取进程句柄失败=%v\n", err)
		return
	}
	defer windows.CloseHandle(pHandle)
	// 读取最终地址指向的值
	err = windows.ReadProcessMemory(
		pHandle,
		address,
		(*byte)(unsafe.Pointer(&value)),
		unsafe.Sizeof(value),
		nil,
	)
	if err != nil {
		return
	}
	return
}

// WriteMemoryValues 向指定地址写入数据
//
//	hwnd - 窗口句柄
//	addresses - 地址
//	values - 数据(float32)
//
// 返回值：
//
//	err - 错误
func WriteMemoryValues(hwnd uintptr, addresses []uintptr, values []float32) error {
	if len(addresses) != len(values) {
		return fmt.Errorf("地址和数据长度不匹配")
	}

	var pid uint32
	windows.GetWindowThreadProcessId(windows.HWND(hwnd), &pid)
	pHandle, err := windows.OpenProcess(0x000F0000|0x00100000|0xFFFF, true, pid)
	if err != nil {
		return fmt.Errorf("获取进程句柄失败=%v", err)
	}
	defer windows.CloseHandle(pHandle)

	for i, address := range addresses {
		valueBytes := *(*[4]byte)(unsafe.Pointer(&values[i]))
		err = windows.WriteProcessMemory(
			pHandle,
			address,
			&valueBytes[0],
			uintptr(len(valueBytes)),
			nil,
		)
		if err != nil {
			return fmt.Errorf("写入内存失败 address=%x, err=%v", address, err)
		}
	}

	return nil
}

// FindAddressByArrayOfByteSync 通过字节序列查找内存地址(同步)
//
//	hwnd - 窗口句柄
//	value - 字节序列
//
// 返回值:
//
//	addresses - 内存地址列表
//	err - 错误信息
func FindAddressByArrayOfByteSync(hwnd uintptr, value []byte) (addresses []uintptr, err error) {
	var pid uint32
	windows.GetWindowThreadProcessId(windows.HWND(hwnd), &pid)

	pHandle, err := windows.OpenProcess(0x0010|0x0020|0x0008|0x0010, false, pid)
	if err != nil {
		return nil, fmt.Errorf("打开进程失败: %v", err)
	}
	defer windows.CloseHandle(pHandle)

	var memInfo windows.MemoryBasicInformation
	address := uintptr(0)

	var wg sync.WaitGroup
	runtime.GOMAXPROCS(int(runtime.NumCPU() / 3))
	addressChan := make(chan uintptr, 100)

	// Goroutine to collect addresses
	go func() {
		for addr := range addressChan {
			addresses = append(addresses, addr)
		}
	}()

	for {
		err := windows.VirtualQueryEx(pHandle, address, &memInfo, unsafe.Sizeof(memInfo))
		if err != nil {
			fmt.Println("VirtualQueryEx error:", err)
			break
		}

		if memInfo.State == windows.MEM_COMMIT &&
			(memInfo.Protect == windows.PAGE_READWRITE ||
				memInfo.Protect == windows.PAGE_READONLY ||
				memInfo.Protect == windows.PAGE_EXECUTE_READWRITE ||
				memInfo.Protect == windows.PAGE_EXECUTE_READ ||
				memInfo.Protect == windows.PAGE_EXECUTE_WRITECOPY) {
			wg.Add(1)
			go func(baseAddr uintptr, regionSize uintptr) {
				defer wg.Done()
				buffer := make([]byte, regionSize)
				var bytesRead uintptr

				err = windows.ReadProcessMemory(pHandle, baseAddr, &buffer[0], uintptr(len(buffer)), &bytesRead)
				if err == nil {
					for i := 0; i <= int(bytesRead)-len(value); i++ {
						if bytes.Equal(buffer[i:i+len(value)], value) {
							addressChan <- baseAddr + uintptr(i)
						}
					}
				}
			}(address, memInfo.RegionSize)
		}

		address += memInfo.RegionSize
	}

	// Wait for all goroutines to finish and then close the channel
	wg.Wait()
	close(addressChan)

	return addresses, nil
}

// GetBaseAddress 获取基址
//
//	hwnd - 窗口句柄
//	offsets - 偏移量
//	pattern - 特征码("AA BB CC")
//	relative - 相对位置(^uintptr(0x48)+1表示-0x48)
//
// 返回值：
// baseAddress - 基址
// err - 错误
func GetBaseAddress(hwnd uintptr, offsets []uintptr, pattern string, relative uintptr) (baseAddress uintptr, err error) {
	patternArr := strings.Split(pattern, " ")
	patternBytes := make([]byte, len(patternArr))
	for i, v := range patternArr {
		fmt.Sscanf(v, "%X", &patternBytes[i])
	}
	fmt.Println("开始搜索内存地址...")
	baseAddresses, err := FindAddressByArrayOfByteSync(hwnd, patternBytes)
	if err != nil {
		fmt.Println("错误:", err)
		return 0, err
	}
	fmt.Println("找到的地址数量:", len(baseAddresses))
	var validAddress uintptr
	// 挨个读取地址的值
	var wg sync.WaitGroup
	for _, base := range baseAddresses {
		wg.Add(1)
		go func(base uintptr) {
			defer wg.Done()
			// ^uintptr(0x48) + 1 相当于 -0x48
			// base += ^uintptr(0x48) + 1
			base += relative
			xPtr, address, _ := ReadMultiLevelPointerVal(hwnd, base, offsets, false)
			if xPtr != 0 && address != 0 {
				validAddress = base
			}
		}(base)
	}
	wg.Wait()
	fmt.Printf("有效地址: %X\n", validAddress)
	if validAddress == 0 {
		return 0, fmt.Errorf("未找到有效地址")
	}
	var pid uint32
	windows.GetWindowThreadProcessId(windows.HWND(hwnd), &pid)

	// 打开进程
	pHandle, err := windows.OpenProcess(0x0010|0x0020|0x0008|0x0010, false, pid)
	if err != nil {
		return 0, fmt.Errorf("打开进程失败: %v", err)
	}
	defer windows.CloseHandle(pHandle)
	// 获取模块基址
	moduleBase, err := getModuleBaseAddress(pHandle, consts.ModuleName)
	if err != nil {
		fmt.Printf("获取模块基址失败=%v\n", err)
		return 0, err
	}
	return validAddress - moduleBase, nil
}
